//------------------------------------------------------------------------------
// Original code by Brendan Tompkins, modified by Ste Brennan for DNAide. 
// http://codebetter.com/blogs/brendan.tompkins/archive/2007/06/14/
// gif-image-color-quantizer-now-with-safe-goodness.aspx
//------------------------------------------------------------------------------
using System;
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;

namespace DNAide.Drawing.Quantization
{
	/// <summary>
	/// Summary description for PaletteQuantizer.
	/// </summary>
	public class PaletteQuantizer : Quantizer
	{
		/// <summary>
		/// Construct the palette quantizer
		/// </summary>
		/// <param name="palette">The color palette to quantize to</param>
		/// <remarks>
		/// Palette quantization only requires a single quantization step
		/// </remarks>
		public PaletteQuantizer(ArrayList palette)
			: base(true)
		{
			_colorMap = new Hashtable();

			pColors = new Color[palette.Count];
			palette.CopyTo(pColors);
		}

		/// <summary>
		/// Override this to process the pixel in the second pass of the algorithm
		/// </summary>
		/// <param name="pixel">The pixel to quantize</param>
		/// <returns>The quantized value</returns>
		protected override byte QuantizePixel(Color32 pixel)
		{
			byte colorIndex = 0;
			int colorHash = pixel.ARGB;

			// Check if the color is in the lookup table
			if (_colorMap.ContainsKey(colorHash))
				colorIndex = (byte)_colorMap[colorHash];
			else
			{
				// Not found - loop through the palette and find the nearest match.
				// Firstly check the alpha value - if 0, lookup the transparent color
				if (0 == pixel.Alpha)
				{
					// Transparent. Lookup the first color with an alpha value of 0
					for (int index = 0; index < pColors.Length; index++)
					{
						if (0 == pColors[index].A)
						{
							colorIndex = (byte)index;
							break;
						}
					}
				}
				else
				{
					// Not transparent...
					int leastDistance = int.MaxValue;
					int red = pixel.Red;
					int green = pixel.Green;
					int blue = pixel.Blue;

					// Loop through the entire palette, looking for the closest color match
					for (int index = 0; index < pColors.Length; index++)
					{
						Color paletteColor = pColors[index];

						int redDistance = paletteColor.R - red;
						int greenDistance = paletteColor.G - green;
						int blueDistance = paletteColor.B - blue;

						int distance = (redDistance * redDistance) +
											 (greenDistance * greenDistance) +
											 (blueDistance * blueDistance);

						if (distance < leastDistance)
						{
							colorIndex = (byte)index;
							leastDistance = distance;

							// And if it's an exact match, exit the loop
							if (0 == distance)
								break;
						}
					}
				}

				// Now I have the color, pop it into the hashtable for next time
				_colorMap.Add(colorHash, colorIndex);
			}

			return colorIndex;
		}

		/// <summary>
		/// Retrieve the palette for the quantized image
		/// </summary>
		/// <param name="palette">Any old palette, this is overrwritten</param>
		/// <returns>The new color palette</returns>
		protected override ColorPalette GetPalette(ColorPalette palette)
		{
			for (int index = 0; index < pColors.Length; index++)
				palette.Entries[index] = pColors[index];

			return palette;
		}

		/// <summary>
		/// Lookup table for colors
		/// </summary>
		private Hashtable _colorMap;

		/// <summary>
		/// List of all colors in the palette
		/// </summary>
		protected Color[] pColors;
	}
}
